A través de los años, la industria del entretenimiento siempre ha ido en crecimiento, siendo la del cine una de las más grandes (solo siendo superada por los videojuegos) en cuanto a ganancias. Es por esto, que el análisis de las películas, de acuerdo a sus diversas cualidades, tales como momento histórico de su desarrollo, presupuesto, género, actores involucrados, entre otros, puede resultar de gran utilidad para la búsqueda del éxito en un medio donde las ‘grandes’ potencias pertenecen a un círculo muy cerrado. Además de que en torno al séptimo arte existen infinidades de personas críticas, se genera la necesidad de información acerca de qué es lo que le gusta a la gente y qué no, es por esto que uno de los objetivos es encontrar el modelo de película que fuera infalible. En el fondo, la existencia de los reviews y su análisis en conjunto, otorga una herramienta poderosa para la posible toma de decisiones de productores, inversionistas, o simplemente si incorporarse a un film por su expectativa calculada a partir de datos históricos y contextualizados. Sin embargo, la existencia de una cantidad de datos tan grandes, no vienen listos para exigirles información implícita de forma simple, por lo que la minería de datos es la herramienta principal en el estudio que será desarrollado en torno a las películas. Además, esta herramienta entrega una forma de conseguir información, que cualquier ser humano le resultaría difícil de visualizar, por ejemplo, la existencia de relaciones entre conceptos que ante nosotros resultan ajenos, para un cierto algoritmo pueden resultar evidentes; en resumen se busca encontrar patrones en la industria que a simple vista son imposibles de distinguir. Por otra parte, las motivaciones vienen principalmente de analizar una temática muy ligada a nuestra cultura como lo son las películas. Es de gran interés para la gente ver las características de las películas con mejor y peor aceptación del público, mostrando los efectos del lenguaje, dinero invertido, duración, etc, en la forma en la que llega a la gente el arte cinematográfica. Este dataset contiene muchos atributos que podrían usarse para poder predecir características relevantes de películas de distintas culturas, presupuesto y temática.
Se quiere entonces comprobar qué atributos y en qué medida afectan al éxito de una película. Para esto, se utilizará "vote_average" (promedio de votos) como parámetro que determinará si una película es exitosa o no. Se realizarán variados experimentos que aportarán a responder la problemática propuesta.
Hay muchos elementos que no tienen sentido económico, por ejemplo que el presupuesto sea igual a 4 en muchas observaciones, ya que es poco probable tener iguales presupuestos para tantas películas y más un valor tan bajo para la industria. Esto hacer pensar que en aquellos datos hubieron errores de medición u obtención, llevando a datos erróneos.
A continuación se mostrará el análisis de los datos efectuado.
from IPython.display import Image
Image("C:\\Users\\Marcela\\Downloads\\mineriadedatos\\img2.png")
Con y sin outliers la distribución se mueve, permitiendo observarla mejor.
Image("C:\\Users\\Marcela\\Downloads\\mineriadedatos\\img3.png")
Image("C:\\Users\\Marcela\\Downloads\\mineriadedatos\\img4.png")
Boxplot de promedio por grupo de presupuesto para dos bases distintas.
Image("C:\\Users\\Marcela\\Downloads\\mineriadedatos\\img5.png")
Image("C:\\Users\\Marcela\\Downloads\\mineriadedatos\\img6.png")
En el primer gráfico se puede apreciar que el voto promedio por película no cambia significativamente entre grupos de presupuesto. Como se había mencionado antes, los datos cuyo presupuesto era 4 millones de dólares o más bajo, podrían estar errados por lo que se filtran y se genera un nuevo gráfico. Esto es lo que se muestra en el segundo boxplot. Como se ve, el primer grupo aumenta un poco su voto promedio pero en general los grupos se mantienen constantes entre ellos.
Image("C:\\Users\\Marcela\\Downloads\\mineriadedatos\\img7.png")
Image("C:\\Users\\Marcela\\Downloads\\mineriadedatos\\img8.png")
En el primer gráfico se muestra la cantidad de votos hechos para un voto promedio (nota promedio), donde cada punto representa una película en específico. En el segundo gráfico se muestra la cantidad de votos usando el mismo subset anterior de presupuesto mayor a 4 millones de dólares. De ambos gráficos es rescatable que bajos puntajes(o notas) fueron determinados por una baja cantidad de votos y que en cambio a medida que aumenta la nota, la cantidad de votantes que la determinaron fue mayor.
Image("C:\Users\Marcela\Downloads\mineriadedatos\img9.png")
Acá se muestra la matriz de covarianza de los atributos numéricos, que serán usados para los experimentos posteriores.
Como se mostró anteriormente, hay muchos datos faltantes o iguales a cero en atributos como Presupuesto o Ingresos, los cuales se eliminarán en ciertas partes del la realización de experimentos. Además de quitar outliers y datos mal ingresados, como películas con índice de popularidad mayores a 300. Es necesario recordar que aquellas partes, se realizarán removiendo las cerca de 35.000 filas que tenían datos faltantes o nulos, por lo que el análisis podría estar sesgado y no es concluyente de exactitud, tal y como en el caso de haber realizado regresiones por ejemplo, no se podría hablar de causalidad, sólo de correlación.
Se realizaron tres experimentos. En primer lugar, se hizo un clustering usando k-means para observar posibles asociaciones entre películas según su nivel de exito en votos y popularidad, y en base a ese clustering se creó una regla de asociación usando el atributo de "genres" (géneros de las películas). Posteriormente, se realizaron algoritmos de clasificación, árboles de decisión y K-nearest neighbors, utilizando las variables numéricas del dataset. Luego, se realizó un árbol de decisión agregando a los atributos anteriores las variables categóricas. El propósito de estos clasificadores es identificar si los atributos disponibles pueden ser usados para poder predecir el voto promedio de una película (criterio de éxito).
A continuación, se realizará el clustering de los datos numéricos, y posteriormente se usará el resultado para poder efectuar la regla de asociación con el atributo "genres".
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
from ast import literal_eval
from sklearn import preprocessing
from sklearn.cluster import KMeans
Leemos el dataset de película:
md = pd.read_excel("C:\\Users\\Marcela\\Downloads\\mineriadedatos\\movies.xlsx")
md_num = md[['title','genres','budget','revenue','popularity','runtime','vote_average','vote_count']]
md_num['genres'] = md_num['genres'].fillna('[]').apply(literal_eval).apply(lambda x: [i['name'] for i in x] if isinstance(x, list) else [])
md_num = md_num.infer_objects()
md_num.dropna(inplace=True)
Leemos los datos desde excel, y transformamos los datos de genero a un formato utilizable en forma de listas. Se eliminan los NA, además de transformar los datos a numericos. Luego filtramos por atributos numericos mayores a 0 y que tengan una cantidad de votos mayores a 10, además de normalizar los datos para utilizar kmeans de forma correcta. Con esto se obtienen 5092 datos de películas.
md_num_filter = md_num[md_num['vote_count']>10]
md_num_filter = md_num_filter[md_num_filter['budget']>0]
md_num_filter = md_num_filter[md_num_filter['revenue']>0]
minmax2 = preprocessing.MinMaxScaler().fit_transform(md_num_filter.drop(['title', 'genres'],axis=1))
md_norm_filter = pd.DataFrame(minmax2, index=md_num_filter.index, columns=md_num_filter.columns[2:])
md_norm_filter.shape
minmax = preprocessing.MinMaxScaler().fit_transform(md_num.drop(['title', 'genres'],axis=1))
md_norm = pd.DataFrame(minmax, index=md_num.index, columns=md_num.columns[2:])
md_num_filter.head()
A continuación, se construirá el grafico de numero de clusters vs score (k-means), para encontrar el codo en tal gráfico, y de esta forma utilizar un $k$ apropiado para el método de clustering k-means.
k = range(1, 15)
kmeans = [KMeans(n_clusters=i) for i in k]
score = [kmeans[i].fit(md_norm).score(md_norm) for i in range(len(kmeans))]
plt.figure(figsize=(15,6))
plt.plot(k,score)
plt.xlabel('Numero de clusters')
plt.ylabel('Score')
plt.title('Elbow')
plt.xticks(range(1, 15), range(1, 15))
plt.show()
Se observa que está cercano al valor 5 ó 6. Se utilizará $k = 5$ para hacer el análisis. Aquí se agregará el label de los clusters encontrados a cada película, para ver estimaciones de cómo se construyeron.
kmeans = KMeans(n_clusters=5, random_state=420)
kmeans.fit(md_norm_filter)
md_num_filter['cluster'] = kmeans.labels_
md_norm_filter['cluster'] = kmeans.labels_
A= md_norm_filter.groupby(['cluster']).mean()
np.sqrt(np.square(A).sum(axis=1))
Aquí vemos las métricas de los cluster resultantes, con esto podemos determinar la forma en que fueron agrupadas, es decir, decimos que a mayor métrica mejor es la película, con esto definimos la pelicula que pertenece al cluster como:
0: mala
1:neutral
2:excelente
3:muy mala
4:buena
movie=md_num_filter[['genres', 'cluster']]
movie.head()
genres= movie['genres'].tolist()
cluster= movie['cluster'].tolist()
final=[]
c=0
for i in genres:
a=i
a.append(str(cluster[c]))
final.append(a)
c+= 1
Creamos una lista que contiene el género y su cluster para poder crear reglas de asociación. De aquí resultan 51 reglas, pero se escogen solo la que contengan como consecuente al cluster, para así caracterizar los géneros con respecto al éxito (de acuerdo al cluster).
from apyori import apriori
association_rules = apriori(final, min_support=0.015, min_confidence=0.4, min_lift=2, min_length=2)
association_results = list(association_rules)
print(len(association_results))
for item in association_results:
pair = item[0]
items = [x for x in pair]
if (items[1]=='0' or items[1]=='1' or items[1]=='2' or items[1]=='3' or items[1]=='4'):
print("Rule: " + items[0] + " -> " + items[1])
print("Support: " + str(item[1]))
print("Confidence: " + str(item[2][0][2]))
print("Lift: " + str(item[2][0][3]))
print("=====================================")
Romance se asocia con el cluster 3 (películas muy malas), con un soporte de un 2,3% y una confianza de 0,71. Las películas de acción se asocian tanto con las películas excelentes como con las malas, sin embargo, las reglas de asociación con el cluster 3 (películas malas) tienen un mayor soporte que las reglas con las películas malas, sin embargo estas últimas contienen una mayor confianza en promedio. Las películas de drama están asociadas al cluster 4 (películas buenas) y 0 (películas malas), con una confianza y soporte parecidos.
Para los procesos de clasificación y previo a este, se construirá una variable que asigna una clase de acuerdo a un rango del atributo promedio de votación (vote_average) y la cual será la variable a clasificar. Se graficaron los votos para tener una idea de cuántas clases crear y distribuir aproximadamente de manera equitativa. La siguiente imagen muestra los cortes, sin embargo, los primeros dos grupos se juntaron debido a que eran menos cantidades de datos.
Image("C:\Users\Marcela\Downloads\mineriadedatos\img10.png")
Finalmente, los grupos creados fueron los siguientes, siendo la clase 1 la de menor voto promedio, es decir la menos valorada y la clase 4 la de mayor voto promedio. Destacar que se usa la misma base filtrada utilizada en la segunda parte del clustering, es decir 5092 filas de datos.
md_voteavg = pd.read_excel("C:\\Users\\Marcela\\Downloads\\mineriadedatos\\peliculas_modificado.xlsx")
md_voteavg_num = md_voteavg[['budget','revenue','popularity','runtime', 'clase_voto']]
md_voteavg_num = md_voteavg_num.infer_objects()
md_voteavg_num.dropna(inplace=True)
md_voteavg_numf = md_voteavg_num[md_voteavg_num['budget']>4]
md_voteavg_numf = md_voteavg_num[md_voteavg_num['revenue']>4]
md_voteavg_numf = md_voteavg_num[md_voteavg_num['popularity']>0]
md_voteavg_numf = md_voteavg_num[md_voteavg_num['runtime']>0]
Se crea el árbol de decisión usando el 60% de los datos como datos de entrenamiento:
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
md_num_train, md_num_test = train_test_split(md_voteavg_numf, test_size=.60, random_state = 37)
md_num_trFeatures = md_num_train.values[: , 0:4]
md_num_trClass = md_num_train.values[: , 4]
clf = DecisionTreeClassifier()
clf.fit(md_num_trFeatures,md_num_trClass)
md_num_teFeatures = md_num_test.values[: , 0:4]
md_num_teClass = md_num_test.values[: , 4]
md_predict = clf.predict(md_num_teFeatures)
print "Accuracy: " + str(accuracy_score(md_num_teClass, md_predict))
print classification_report(md_num_teClass, md_predict)
Ahora se hará el clasificador con KNN:
clf_knn = KNeighborsClassifier(n_neighbors=5)
clf_knn.fit(md_num_trFeatures,md_num_trClass)
md_predict = clf_knn.predict(md_num_teFeatures)
print "Accuracy: " + str(accuracy_score(md_num_teClass, md_predict))
print(classification_report(md_num_teClass, md_predict))
Los resultados obtenidos no son muy positivos, el accuracy, la precisión y el recall son todos muy bajos. Hasta ahora no se obtiene un clasificador muy bueno utilizando solo los datos numéricos.
Ahora se repite lo anterior pero quitando budget y revenue:
md_voteavg = pd.read_excel("C:\\Users\\Marcela\\Downloads\\mineriadedatos\\peliculas_modificado.xlsx")
md_voteavg_num2 = md_voteavg[['popularity','runtime', 'clase_voto']]
md_voteavg_num2 = md_voteavg_num2.infer_objects()
md_voteavg_num2.dropna(inplace=True)
md_voteavg_num2 = md_voteavg_num2[md_voteavg_num2["popularity"]>0]
md_voteavg_num2 = md_voteavg_num2[md_voteavg_num2["runtime"]>0]
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
md_num_train, md_num_test = train_test_split(md_voteavg_num2, test_size=.60, random_state = 37)
md_num_trFeatures = md_num_train.values[: , 0:2]
md_num_trClass = md_num_train.values[: , 2]
clf = DecisionTreeClassifier()
clf.fit(md_num_trFeatures,md_num_trClass)
md_num_teFeatures = md_num_test.values[: , 0:2]
md_num_teClass = md_num_test.values[: , 2]
md_predict = clf.predict(md_num_teFeatures)
print accuracy_score(md_num_teClass, md_predict)
print classification_report(md_num_teClass, md_predict)
clf_knn = KNeighborsClassifier(n_neighbors=5)
clf_knn.fit(md_num_trFeatures,md_num_trClass)
md_predict = clf_knn.predict(md_num_teFeatures)
print accuracy_score(md_num_teClass, md_predict)
print(classification_report(md_num_teClass, md_predict))
A partir de estos resultados, se puede ver que la accuracy, precisión y recall no difieren en gran medida. Todos los clasificadores hechos no han tenido buenos resultados, por lo que se puede deducir que las variables numéricas no son suficientes para poder efectuar un buen clasificador.